home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 264_01 / cp.c < prev    next >
Text File  |  1980-01-01  |  10KB  |  431 lines

  1. /*
  2.  * cp - copy files and directories
  3.  *
  4.  * Usage: cp [-fiv] [-] file file
  5.  *        cp [-fiv] [-] file... dir
  6.  *        cp [-fiv] -r [-] file|dir... dir
  7.  * Destination can be a drive, a directory, or a filename.
  8.  * Makes only minimal effort to stop you from destroying a file (or
  9.  * directory!) by copying it onto itself.
  10.  *
  11.  * Flags:
  12.  * -    interpret following arguments starting with '-' as filenames
  13.  * -f   force; don't report errors, override readonly attribute
  14.  * -i   interactive; asks whether to overwrite existing files
  15.  * -r   recursively copy arguments which are directories
  16.  * -v   verbose; display name of each file as it's copied
  17.  *
  18.  * This program is in the public domain.
  19.  * David MacKenzie
  20.  * 6522 Elgin Lane
  21.  * Bethesda, MD 20817
  22.  *
  23.  * Latest revision: 05/07/88
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <stat.h>
  28. #include <fcntl.h>
  29. #include <ctype.h>
  30. #ifndef tolower(c)
  31. #define tolower(c) _tolower(c)
  32. #endif
  33. #include "getdir.h"
  34.  
  35. /* Values for the first argument of ftime(). */
  36. #define FT_GET 0        /* Return date/time. */
  37. #define FT_SET 1        /* Set date/time to third argument. */
  38.  
  39. #define COPYSIZE (1024 * 16)    /* Number of bytes to copy at once. */
  40.  
  41. #define MAXPATH 125        /* Arbitrary internal limit on path lengths. */
  42.  
  43. int     fflag = 0;        /* Force success? */
  44. int     iflag = 0;        /* Interactive (query before copying)? */
  45. int     rflag = 0;        /* Copy subdirectories recursively? */
  46. int     vflag = 0;        /* Verbose (display names as copied)? */
  47.  
  48. struct stat ssbuf, dsbuf, tsbuf;/* Source, destination, temp. */
  49.  
  50. _main(argc, argv)
  51.     int     argc;
  52.     char  **argv;
  53. {
  54.     void    usage(), cp();
  55.     int     optind;        /* Loop index. */
  56.  
  57.     for (optind = 1; optind < argc && *argv[optind] == '-'; ++optind) {
  58.     if (argv[optind][1] == 0)
  59.         break;
  60.     else
  61.         while (*++argv[optind])
  62.         switch (*argv[optind]) {
  63.         case 'f':
  64.             fflag = 1;
  65.             break;
  66.         case 'i':
  67.             iflag = 1;
  68.             break;
  69.         case 'r':
  70.             rflag = 1;
  71.             break;
  72.         case 'v':
  73.             vflag = 1;
  74.             break;
  75.         default:
  76.             usage();
  77.             break;
  78.         }
  79.     }
  80.  
  81.     if (optind == argc || optind == argc - 1)
  82.     usage();
  83.  
  84.     if (dstat(argv[argc - 1], &dsbuf) == 0 && !isdir(&dsbuf) &&
  85.     optind != argc - 2)
  86.     /* Can't copy multiple files/directories onto one file. */
  87.     usage();
  88.  
  89.     for (; optind < argc - 1; ++optind)
  90.     cp(argv[optind], argv[argc - 1]);
  91.  
  92.     exit(0);
  93. }
  94.  
  95. /*
  96.  * Source is a file or directory, dest is a file or directory.
  97.  */
  98. void
  99. cp(source, dest)
  100.     char   *source;
  101.     char   *dest;
  102. {
  103.     void    cpdir(), cpfile();
  104.     char   *strtolower();
  105.  
  106.     (void) strtolower(source);
  107.     (void) strtolower(dest);
  108.     if (vflag)
  109.     printf("%s\n", source);
  110.  
  111.     if (!strcmp(source, dest)) {
  112.     if (!fflag)
  113.         fprintf(stderr, "%s and %s are identical\n", source, dest);
  114.     return;
  115.     }
  116.     if (dstat(source, &ssbuf) == -1) {
  117.     if (!fflag)
  118.         fprintf(stderr, "%s: File not found\n", source);
  119.     return;
  120.     }
  121.     if (isdir(&ssbuf)) {
  122.     if (!rflag) {
  123.         if (!fflag)
  124.         fprintf(stderr, "%s: Is a directory; not copied\n", source);
  125.         return;
  126.     }
  127.     cpdir(source, dest);
  128.     } else
  129.     cpfile(source, dest);
  130. }
  131.  
  132. /*
  133.  * Recursively copy the contents of a directory.
  134.  * Source is a directory, dest is a directory, either existing or new.
  135.  * Only used when -r flag is given.
  136.  */
  137. void
  138. cpdir(source, dest)
  139.     char   *source;
  140.     char   *dest;
  141. {
  142.     void    cpfile();
  143.     char   *basename(), *concat();
  144.     char    dirbuf[512];    /* Buffer for disk input. */
  145.     struct ffblk *dir = (struct ffblk *) dirbuf;
  146.     char    savedest[MAXPATH];    /* Destination if must make its name. */
  147.     char    srcpath[MAXPATH];    /* Path of globbed source arguments. */
  148.     char   *tail;        /* Tail component of srcpath. */
  149.     int     i;            /* Loop control. */
  150.  
  151.     if (isdir(&dsbuf)) {
  152.     (void) strcpy(savedest, concat(dest, basename(source)));
  153.     dest = savedest;
  154.     if (dstat(dest, &tsbuf) == 0 && isdir(&tsbuf)) {
  155.         if (!fflag)
  156.         fprintf(stderr, "%s: Is a directory\n", dest);
  157.         return;
  158.     }
  159.     }
  160.     if (mkdir(dest) == -1) {
  161.     perr(dest);
  162.     return;
  163.     }
  164.     /*
  165.      * Save any leading drive and/or path in srcpath because the MS-DOS
  166.      * globbing routines return only the base of the filenames. 
  167.      */
  168.     (void) strcpy(srcpath, concat(source, "*.*"));
  169.     tail = basename(srcpath);
  170.  
  171.     /* Must reset the disk transfer area because stat() uses it. */
  172.     for (setdta(dirbuf), i = getfirst(srcpath, FA_ALL); !i;
  173.     setdta(dirbuf), i = getnext(srcpath, FA_ALL)) {
  174.     if (dir->ff_fname[0] != '.') {
  175.         (void) strcpy(tail, dir->ff_fname);
  176.         cp(srcpath, dest);
  177.     }
  178.     }
  179. }
  180.  
  181. /*
  182.  * Copy a file.
  183.  * Source is a file, dest is either a file (existing or new) or a directory
  184.  * (which must exist).
  185.  */
  186. void
  187. cpfile(source, dest)
  188.     char   *source;
  189.     char   *dest;
  190. {
  191.     long    ftime();
  192.     char   *basename(), *concat(), *Malloc();
  193.     struct stat *statp;
  194.     char   *buf;        /* Copy buffer. */
  195.     int     in, out;        /* File descriptors. */
  196.     int     n;            /* Number of bytes read. */
  197.     int     cperr = 0;        /* Error during copy? */
  198.  
  199.     statp = &dsbuf;
  200.     if (isdir(&dsbuf)) {
  201.     dest = concat(dest, basename(source));
  202.     if (dstat(dest, &tsbuf) == 0 && isdir(&tsbuf)) {
  203.         if (!fflag)
  204.         fprintf(stderr, "%s: Is a directory\n", dest);
  205.         return;
  206.     }
  207.     statp = &tsbuf;
  208.     }
  209.     if (!permit(dest, statp) || !writable(dest, statp))
  210.     return;
  211.  
  212.     if ((in = open(source, O_RDONLY)) == -1) {
  213.     perr(source);
  214.     return;
  215.     }
  216.     if ((out = open(dest, O_WRONLY | O_CREAT | O_TRUNC)) == -1) {
  217.     (void) close(in);
  218.     perr(dest);
  219.     return;
  220.     }
  221.     buf = Malloc(COPYSIZE);
  222.     while ((n = read(in, buf, COPYSIZE)) > 0)
  223.     if (write(out, buf, n) != n)
  224.         break;
  225.  
  226.     (void) free(buf);
  227.     /* Preserve readonly and other modes. */
  228.     if (chmod(dest, ssbuf.st_attr) == -1)
  229.     perr("chmod");
  230.     /* Set date and time of dest to those of source. */
  231.     if (ftime(FT_SET, out, ssbuf.st_mtime))
  232.     perr("ftime");
  233.  
  234.     if (close(in) == -1 || n < 0) {
  235.     if (!fflag)
  236.         fprintf(stderr, "%s: Read error\n", source);
  237.     cperr = 1;
  238.     }
  239.     if (close(out) == -1 || n > 0) {
  240.     if (!fflag)
  241.         fprintf(stderr, "%s: Write error\n", dest);
  242.     cperr = 1;
  243.     }
  244.     if (cperr)
  245.     (void) unlink(dest);
  246. }
  247.  
  248. /*
  249.  * Determine if overwriting destination file is permissible.
  250.  * Returns 1 if permission granted, 0 if not.
  251.  */
  252. permit(path, statp)
  253.     char   *path;
  254.     struct stat *statp;
  255. {
  256.     int     r;            /* Response character. */
  257.     int     c;            /* One character of input. */
  258.  
  259.     if (!iflag || fflag)
  260.     return 1;
  261.     /* If it doesn't exist, permission granted. */
  262.     if (statp->st_size == -1L || !isatty(0))
  263.     return 1;
  264.     fprintf(stderr, "overwrite %s? ", path);
  265.     fflush(stderr);
  266.     c = r = getchar();
  267.     while (c != '\n' && c != EOF)
  268.     c = getchar();
  269.     return r == 'y' || r == 'Y';
  270. }
  271.  
  272. /*
  273.  * Return 1 if write permission is granted on the path, 0 otherwise.
  274.  */
  275. writable(path, statp)
  276.     char   *path;
  277.     struct stat *statp;
  278. {
  279.     int     r;            /* Response character. */
  280.     int     c;            /* One character of input. */
  281.  
  282.     /* If it doesn't exist, it's writable. */
  283.     if (statp->st_size == -1L)
  284.     return 1;
  285.     if (statp->st_attr & ST_RDONLY) {
  286.     if (!fflag && isatty(0)) {
  287.         fprintf(stderr, "override protection for %s? ", path);
  288.         fflush(stderr);
  289.         c = r = getchar();
  290.         while (c != '\n' && c != EOF)
  291.         c = getchar();
  292.         if (r != 'y' && r != 'Y')
  293.         return 0;
  294.     }
  295.     return chmod(path, statp->st_attr & ~ST_RDONLY) != -1;
  296.     }
  297.     return 1;
  298. }
  299.  
  300. /*
  301.  * Convert a string to lowercase.  Return the argument.
  302.  */
  303. char   *
  304. strtolower(s)
  305.     char   *s;
  306. {
  307.     char   *t;
  308.  
  309.     for (t = s; *t; ++t)
  310.     if (isascii(*t) && isupper(*t) && *t != '_')
  311.         *t = tolower(*t);
  312.     return s;
  313. }
  314.  
  315. /*
  316.  * Return true if the path has the form "d:" or "\" or "d:\".
  317.  */
  318. isroot(p)
  319.     char   *p;
  320. {
  321.     int     i = strlen(p);
  322.  
  323.     return i == 2 && p[1] == ':' ||
  324.     !strcmp(p, "\\") ||
  325.     i == 3 && !strcmp(p + 1, ":\\");
  326. }
  327.  
  328. /*
  329.  * Return true if the path has the form:
  330.  * ".." or "d:.." or "."